package net.bcxuexi.socket;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import net.bcxuexi.config.Config;
import net.bcxuexi.config.Gloab;
import net.bcxuexi.exception.MyException;
import net.bcxuexi.socket.model.IPort;
import net.bcxuexi.socket.model.SocketModel;
import net.bcxuexi.socket.stream.ReadStream;
import net.bcxuexi.socket.stream.StreamData;
import net.bcxuexi.socket.stream.WriteStream;
import net.bcxuexi.socket.stream.outwrap.OutputDataWrap;
import net.bcxuexi.tools.IdGen;
import net.bcxuexi.tools.MyLog;
import net.bcxuexi.tools.StringUtils;

public class SocketManager {
	private static SocketManager socketManager;
	private SocketModel controlSocketModel;
	// data socket池
	private List<SocketModel> socketModelPool;
	private int maximumSocketSize = Integer.valueOf(Gloab.getInstance()
			.getConfig("maximumSocketSize"));// 最大socket数
	private int coreSocketSize = Integer.valueOf(Gloab.getInstance().getConfig(
			"coreSocketSize"));// 相当于最小socket数
	/** 内网需要映射的端口 */
	private List<IPort> iports = new ArrayList<IPort>();
	/**
	 * 内网通信socket
	 */
	private List<SocketModel> innerSocketModelPool = new ArrayList<SocketModel>();


	private SocketManager() {

	}

	private void init() {
		// 创建控制socket
		controlSocketMonitor.run();
		// 初始化线程
		socketModelPool = new ArrayList<SocketModel>(maximumSocketSize);
		for (int i = 0; i < coreSocketSize; i++) {
			SocketModel socketModel = createDataSocket();
		}
		// 控制socket监控服务
		Timer timer = new Timer();
		timer.schedule(controlSocketMonitor, 1000, 2000);
		// 监控data socket服务
		Timer timer2 = new Timer();
		timer2.schedule(dataSocketMonitor, 1000, 3000);
		// 监控inner socket服务
		Timer timer3 = new Timer();
		timer3.schedule(innerSocketMonitor, 1000, 10000);
	}

	/**
	 * 控制socket监控服务。 如果controlSocket出现问题，则必须关闭所有socket及相关线程，重新创建controlSocket
	 */
	private TimerTask controlSocketMonitor = new TimerTask() {
		@Override
		public void run() {
			while (true) {
				if (controlSocketModel == null
						|| !controlSocketModel.isActive()) {
					// 创建中心控制服务socket
					try {
						controlSocketModel = createControlSocket();
						if(controlSocketModel==null||!controlSocketModel.isActive()){
							MyLog.info("建立控制连接失败，等待2秒后再次尝试建立");
							try {
								Thread.sleep(2000);
								continue;
							} catch (InterruptedException ex) {
								ex.printStackTrace();
							}
							continue;
						}else{
							break;
						}
						
					} catch (MyException e) {
						MyLog.info("建立控制连接失败，等待2秒后再次尝试建立");
						try {
							Thread.sleep(2000);
							continue;
						} catch (InterruptedException ex) {
							ex.printStackTrace();
						}
					}
				} else {
					//发送心跳消息
					OutputDataWrap outputDataWrap = new OutputDataWrap();
					StreamData streamData = outputDataWrap.csHeartbeat(controlSocketModel.getConnId());
					streamData.setSocketModel(controlSocketModel);
					try {
						controlSocketModel.getWriteStream().addData(streamData);
					} catch (Exception e) {
						e.printStackTrace();
					}
					break;
				}
			}
		}
	};
	/**
	 * 连接池socket监控服务。
	 */
	private TimerTask dataSocketMonitor = new TimerTask() {
		@Override
		public void run() {
			try {
				System.out.println("data socket数="+socketModelPool.size());
				for (Iterator iterator = socketModelPool.iterator(); iterator
						.hasNext();) {
					SocketModel socketModel = (SocketModel) iterator.next();
					if (!socketModel.isActive()) {
						iterator.remove();
						continue;
					}
					//发送心跳消息
					OutputDataWrap outputDataWrap = new OutputDataWrap();
					StreamData streamData = outputDataWrap.csHeartbeat(socketModel.getConnId());
					streamData.setSocketModel(socketModel);
					socketModel.getWriteStream().addData(streamData);
				}
				if(socketModelPool.size()==0){
					MyLog.info("data socket数为0,重新创建datasocket");
					createDataSocket();
				}
			} catch (Exception e) {
				MyLog.info("dataSocketMonitor 监控服务错误.");
				e.printStackTrace();
			}
		}
	};
	/**
	 * inner socket监控服务。
	 */
	private TimerTask innerSocketMonitor = new TimerTask() {
		@Override
		public void run() {
			try {
				System.out.println("inner socket数="+innerSocketModelPool.size());
				for (Iterator iterator = innerSocketModelPool.iterator(); iterator
						.hasNext();) {
					SocketModel socketModel = (SocketModel) iterator.next();
					if (!socketModel.isActive()) {
						iterator.remove();
						continue;
					}
				}
			} catch (Exception e) {
				MyLog.info("innerSocketMonitor 监控服务错误.");
				e.printStackTrace();
			}
		}
	};

	/**
	 * 创建一个socket,控制类型的socket只允许创建一个
	 * 
	 * @param port
	 *            serversocket端口
	 * @param type
	 *            serversocket类型
	 * @throws MyException
	 */
	private SocketModel createSocket(String serverIp, int port, String type)
			throws MyException {
		// 控制类型的socket只允许创建一个
		if (Config.SocketType.control.equals(type)
				&& controlSocketModel != null && !controlSocketModel.isActive()) {
			return controlSocketModel;
		}
		try {
			InetAddress inetAddress = InetAddress.getByName(serverIp);
			Socket serverSocket = new Socket(inetAddress, port);
			// 创建socketmodel
			SocketModel socketModel = new SocketModel();
			socketModel.setSocket(serverSocket);
			socketModel.setType(type);
			// 输入输出流处理
			ReadStream readStream = new ReadStream(socketModel);
			WriteStream writeStream = new WriteStream(socketModel);
			socketModel.setReadStream(readStream);
			socketModel.setWriteStream(writeStream);
			readStream.start();
			writeStream.start();
			return socketModel;
		} catch (IOException e) {
			throw new MyException("", e.getMessage(), e);
		}
	}

	/**
	 * 创建控制通信socket
	 * 
	 * @return
	 */
	private SocketModel createControlSocket() throws MyException {
		if (controlSocketModel != null && controlSocketModel.isActive()) {
			return controlSocketModel;
		}
		// 创建中心控制服务socket
		try {
			controlSocketModel = createSocket(Gloab.getInstance()
					.getServerIp(), Gloab.getInstance().getServerPort(),
					Config.SocketType.control);
			MyLog.info("建立控制连接成功!");
			// 发送连接请求信息
			OutputDataWrap outputDataWrap = new OutputDataWrap();
			StreamData streamData = outputDataWrap.csControlConn(controlSocketModel);
			try {
				MyLog.info("发送控制连接请求");
				controlSocketModel.getWriteStream().addData(streamData);
				return controlSocketModel;
			} catch (Exception e) {
				e.printStackTrace();
				if(controlSocketModel!=null){
					controlSocketModel.closeSocket("发送控制连接请求异常，关闭连接");
				}
				
			}
		} catch (MyException e) {
			e.printStackTrace();
			throw new MyException("建立控制连接失败");
		}
		return null;
	}

	/**
	 * 创建与服务器通信的socket
	 * 
	 * @return
	 */
	private SocketModel createDataSocket() {
		// 创建中心控制服务socket
		try {
			SocketModel socketModel = createSocket(Gloab.getInstance()
					.getServerIp(), Gloab.getInstance().getServerPort(),
					Config.SocketType.data);
			// 发送连接请求信息
			OutputDataWrap outputDataWrap = new OutputDataWrap();
			StreamData streamData = outputDataWrap.csDataConn(socketModel);
			try {
				socketModel.getWriteStream().addData(streamData);
				socketModelPool.add(socketModel);
				return socketModel;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} catch (MyException e) {
			MyLog.info("建立data socket 连接失败。");
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 建立与内网服务器通信的socket
	 * 
	 * @param proxyConnId
	 *            外部代理socket的connId
	 * @param innerIp
	 * @param innerPort
	 */
	public SocketModel createInnerSocket(String proxyConnId, String innerIp,
			int innerPort) {
		// 检查是否已为proxyConnId建立了连接
		SocketModel socketModel = findIPortSocketByProxyConnId(proxyConnId);
		if (socketModel != null) {
			return socketModel;
		}
		try {
			// 建立内网socket
			Socket innerSocket = new Socket(innerIp, innerPort);
			// 创建socketmodel
			SocketModel innerSocketModel = new SocketModel();
			innerSocketModel.setConnId(IdGen.generateShortUuid());
			innerSocketModel.setSocket(innerSocket);
			innerSocketModel.setType(Config.SocketType.inner);
			innerSocketModel.setInnerIp(innerIp);
			innerSocketModel.setInnerPort(innerPort);
			innerSocketModel.setProxyConnId(proxyConnId);

			// 输入输出流处理
			ReadStream readStream = new ReadStream(innerSocketModel);
			WriteStream writeStream = new WriteStream(innerSocketModel);
			innerSocketModel.setReadStream(readStream);
			innerSocketModel.setWriteStream(writeStream);
			readStream.start();
			writeStream.start();
			innerSocketModelPool.add(innerSocketModel);
			return innerSocketModel;
		} catch (IOException e) {// 有可能内网服务未打开
			MyLog.info("iport内网连接创建失败。proxyConnId="+proxyConnId+" " + innerIp+":"+innerPort);
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 通过ProxyConnId查找inner socket
	 * @param innerIp
	 * @param innerPort
	 * @return
	 */
	public SocketModel findIPortSocketByProxyConnId(String proxyConnId) {
		for (Iterator iterator = innerSocketModelPool.iterator(); iterator.hasNext();) {
			SocketModel socketModel = (SocketModel) iterator.next();
			if (proxyConnId.equals(socketModel.getProxyConnId())) {
				if(socketModel.isActive()){
					return socketModel;
				}else{
					iterator.remove();
				}
			}
		}
		return null;
	}

	/**
	 * 查找当前发送任务为空的socket； 如果没有且池未满 则创建socket； 如果池已满 则取发送任务最少的socket
	 */
	public SocketModel getDataSocket() {
		// 任务最少的socket
		SocketModel minSocketModel = null;
		for (Iterator iterator = socketModelPool.iterator(); iterator.hasNext();) {
			SocketModel socketModel = (SocketModel) iterator.next();
			if (!socketModel.isActive()) {
				iterator.remove();
				continue;
			}
			int size = socketModel.getWriteStream().getSize();
			// 发送任务为空的socket
			if (size == 0) {
				return socketModel;
			} else {
				if (size < minSocketModel.getWriteStream().getSize()) {
					minSocketModel = socketModel;
				}
			}
		}
		// 如果没有且池未满 则创建socket；
		if (socketModelPool.size() < maximumSocketSize) {
			minSocketModel = createDataSocket();
		}
		return minSocketModel;
	}
	
	/**
	 * 通过connId查询socket
	 */
	public SocketModel getSocketModelById(String id) {
		if (StringUtils.isBlack(id)) {
			return null;
		}
		if(controlSocketModel!=null&&id.equals(controlSocketModel.getConnId())){
			return controlSocketModel;
		}
		
		for (SocketModel socketModel : socketModelPool) {
			if (id.equals(socketModel.getConnId())) {
				return socketModel;
			}
		}
		
		for (SocketModel socketModel : innerSocketModelPool) {
			if (id.equals(socketModel.getConnId())) {
				return socketModel;
			}
		}
		return null;
	}

	/**
	 * 查找正在处理proxyConnId的socket,没有则新建
	 * 
	 * @param proxyConnId
	 * @return
	 */
	public SocketModel getDataSocketByProxyConnId(String proxyConnId) {
		for (Iterator iterator = socketModelPool.iterator(); iterator.hasNext();) {
			SocketModel socketModel = (SocketModel) iterator.next();
			if (!socketModel.isActive()) {
				iterator.remove();
				continue;
			}
			boolean has = socketModel.getWriteStream().hasProxyConnId(
					proxyConnId);
			if (has) {
				return socketModel;
			}
		}
		SocketModel socketModel = getDataSocket();
		return socketModel;
	}
	public SocketModel getInnerSocketByProxyConnId(String proxyConnId) {
		for (Iterator iterator = innerSocketModelPool.iterator(); iterator.hasNext();) {
			SocketModel socketModel = (SocketModel) iterator.next();
			if (!socketModel.isActive()) {
				iterator.remove();
				continue;
			}
			if(proxyConnId.equals(socketModel.getProxyConnId())){
				return socketModel;
			}
		}
		return null;
	}

	public void addIports(List<IPort> iportList){
		//需要新增的iport
		List<IPort> addIports = new ArrayList<IPort>();
		for (Iterator iterator = iportList.iterator(); iterator.hasNext();) {
			IPort iport = (IPort) iterator.next();
			boolean has = iports.contains(iport);
			if(!has){
				addIports.add(iport);
			}
		}
		//需要删除的iport
		List<IPort> delIports = new ArrayList<IPort>();
		for (Iterator iterator = iports.iterator(); iterator.hasNext();) {
			IPort iport = (IPort) iterator.next();
			boolean has = iportList.contains(iport);
			if(!has){
				delIports.add(iport);
			}
		}
		//删除iport
		iports.removeAll(delIports);
		//添加iport
		iports.addAll(addIports);
		
	}
	
	public void closeInnerSocketByProxyConnId(String proxyConnId){
		SocketModel innerSocketModel = getInnerSocketByProxyConnId(proxyConnId);
		if(innerSocketModel!=null){
			innerSocketModel.closeSocket("根据proxyConnId("+proxyConnId+")关闭 innersocket:"+innerSocketModel.toString());
		}
	}

	public static synchronized SocketManager getInstance() {
		if (socketManager == null) {
			socketManager = new SocketManager();
			socketManager.init();
		}
		return socketManager;
	}
}
